/*
 * x86_64/ioapic.hh - I/O APIC data types and functions.
 * Copyright (C) 2007  Mikhail Vorozhtsov
 * See the LICENSE section of the README file for details.
 */

/* $Id$ */

#ifndef HEADER_APIC_IOAPIC_HH
#define HEADER_APIC_IOAPIC_HH

START_NAMESPACE (x86_64)

struct ioapic_t
{
public:
  enum reg_t
  {
    REG_ID = 0,
    REG_VERSION = 1,
    REG_ARB = 2,
    REG_RE0_LO = 0x10,
    REG_RE0_HI = 0x11
  };

  enum delivery_mode_t
  {
    DM_FIXED = 0,
    DM_LOWEST_PRI = 1,
    DM_SMI = 2,
    DM_NMI = 4,
    DM_INIT = 5,
    DM_EXT_INT = 7
  };

  enum polarity_t
  {
    POLARITY_HI = 0,
    POLARITY_LO = 1
  };

  enum trig_mode_t
  {
    TRIG_MODE_EDGE = 0,
    TRIG_MODE_LEVEL = 1
  };

  union id_t
  {
    struct
      {
        u32_t : 24;
        u32_t id : 4;
        u32_t : 4;
      };
    u32_t raw;

    UTILS_RAW_ZERO (id_t, u32_t)
    UTILS_SETTER (id_t, u8_t, id)
  } PACKED;

  union version_t
  {
    struct
      {
        u32_t version : 8;
        u32_t : 8;
        u32_t MRE : 8;
        u32_t : 8;
      };
    u32_t raw;

    UTILS_RAW_ZERO (version_t, u32_t)
  } PACKED;

  union arb_t
  {
    struct
      {
        u32_t : 24;
        u32_t id : 4;
        u32_t : 4;
      };
    u32_t raw;

    UTILS_RAW_ZERO (arb_t, u32_t)

    UTILS_SETTER (arb_t, u8_t, id)
  } PACKED;

  union re_t
  {
    struct
      {
        u64_t vector : 8;
        u64_t DM : 3;
        u64_t DSTM : 1;
        u64_t DS : 1;
        u64_t polarity : 1;
        u64_t RIRR : 1;
        u64_t TM : 1;
        u64_t M : 1;
        u64_t : 39;
        u64_t DST : 8;
      };
    u64_t raw;
    u32_t words[2];

    UTILS_RAW_ZERO (re_t, u64_t)

    UTILS_SETTER (re_t, u8_t, vector)
    UTILS_SETTER (re_t, delivery_mode_t, DM)
    UTILS_BOOL_SETTER (re_t, DSTM)
    UTILS_BOOL_SETTER (re_t, DS)
    UTILS_SETTER (re_t, polarity_t, polarity)
    UTILS_BOOL_SETTER (re_t, RIRR)
    UTILS_SETTER (re_t, trig_mode_t, TM)
    UTILS_BOOL_SETTER (re_t, M)
    UTILS_SETTER (re_t, u8_t, DST)
  } PACKED;

private:
  void *addr;

public:
  INLINE
  ioapic_t (void *addr)
  {
    this->addr = addr;
  }

private:
  INLINE
  u32_t
  read (reg_t reg)
  {
    *((volatile u32_t *) addr) = reg;
    return *((volatile u32_t *) (addr + 0x10));
  }

  INLINE
  void
  write (reg_t reg, u32_t value)
  {
    *((volatile u32_t *) addr) = reg;
    *((volatile u32_t *) (addr + 0x10)) = value;
  }

public:
#define __X86_64_IOAPIC_GETTER(REG,TYPE,NAME) \
  INLINE \
  TYPE \
  get_ ## NAME () \
  { \
    return read (REG_ ## REG); \
  }

#define __X86_64_IOAPIC_GETTER_SETTER(REG,TYPE,NAME) \
  __X86_64_IOAPIC_GETTER (REG, TYPE, NAME) \
  \
  INLINE \
  void \
  set_ ## NAME (TYPE x) \
  { \
    write (REG_ ## REG, x); \
  }

  __X86_64_IOAPIC_GETTER_SETTER (ID, id_t, id)
  __X86_64_IOAPIC_GETTER_SETTER (VERSION, version_t, version)
  __X86_64_IOAPIC_GETTER (ARB, arb_t, arb)

#undef __X86_64_IOAPIC_GETTER_SETTER
#undef __X86_64_IOAPIC_GETTER

  INLINE
  re_t
  get_re (u8_t nr)
  {
    re_t result;
    result.words[0] = read ((reg_t) (REG_RE0_LO + nr * 2));
    result.words[1] = read ((reg_t) (REG_RE0_HI + nr * 2));
    return result;
  }

  INLINE
  void
  set_re (u8_t nr, re_t re)
  {
    write ((reg_t) (REG_RE0_HI + nr * 2), re.words[1]);
    write ((reg_t) (REG_RE0_LO + nr * 2), re.words[0]);
  }
};

END_NAMESPACE

#endif /* HEADER_APIC_IOAPIC_HH */

